/*******************************************************************************
 * Copyright (c) 2011 Irineu Martins de Lima Moura (imlm2).
 * This file is part of JBeat.
 * 
 *     JBeat is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     JBeat is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with JBeat.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Irineu Martins de Lima Moura (imlm2) - initial API and implementation
 ******************************************************************************/
package br.ufpe.cin.imlm2.jbeat.soot.throwableset.tags;

import java.util.*;

import soot.SootClass;
import soot.SootMethod;
import soot.tagkit.AttributeValueException;
import soot.tagkit.Tag;

/**
 * A tag that contains all the possible exceptions that a given method may
 * throw.
 * 
 * @author Irineu
 * 
 */
public class ThrowableSetTag implements Tag {

	public static final String TAG_NAME = "ThrowableSetTag";

	private Set<SootClass> generated;
	private Set<SootClass> thrown;
	private Map<SootClass, Set<SootMethod>> propagated;
	private Map<SootClass, Set<SootMethod>> rethrown;

	public ThrowableSetTag() {
		this.generated = new HashSet<SootClass>();
		this.thrown = new HashSet<SootClass>();
		this.propagated = new HashMap<SootClass, Set<SootMethod>>();
		this.rethrown = new HashMap<SootClass, Set<SootMethod>>();
	}

	public void addGeneratedException(SootClass e) {
		this.generated.add(e);
	}
	
	public void addAllGeneratedExceptions(Set<SootClass> exceptions) {
		this.generated.addAll(exceptions);
	}

	public void removeGeneratedException(SootClass e) {
		this.generated.remove(e);
	}

	public void addPropagatedException(SootClass exception, SootMethod source) {
		putInMap(this.propagated,exception, source);
	}

	public void addAllPropagatedExceptions(Set<SootClass> exceptions,
			SootMethod source) {
		for (SootClass exception : exceptions) {
			this.addPropagatedException(exception, source);
		}
	}
	
	public void removePropagatedException(SootClass exception) {
		this.propagated.remove(exception);
	}
	
	public void addThrownException(SootClass thrown) {
		this.thrown.add(thrown);
	}
	
	public void addAllThrownExceptions(Set<SootClass> exceptions) {
		this.thrown.addAll(exceptions);
	}

	public void removeThrownException(SootClass exception) {
		this.thrown.remove(exception);
	}

	public void addRethrownException(SootClass exception, SootMethod source) {
		this.putInMap(this.rethrown, exception, source);
	}
	
	public void addAllRethrownExceptions(Set<SootClass> exceptions,
			SootMethod source) {
		for (SootClass sootClass : exceptions) {
			this.addRethrownException(sootClass, source);
		}
	}

	public void removeRethrownException(SootClass exception) {
		this.rethrown.remove(exception);
	}
	
	public Set<SootClass> getThrowableSet() {
		Set<SootClass> throwableSet = new HashSet<SootClass>();
		throwableSet.addAll(this.generated);
		throwableSet.addAll(this.thrown);
		throwableSet.addAll(this.propagated.keySet());
		throwableSet.addAll(this.rethrown.keySet());
		return throwableSet;
	}

	public Set<SootClass> getAllGeneratedExceptions() {
		return this.generated;
	}

	public Set<SootClass> getAllThrownExceptions() {
		return this.thrown;
	}

	public Map<SootClass, Set<SootMethod>> getPropagatedMap() {
		return this.propagated;
	}

	public Set<SootClass> getAllPropagatedExceptions() {
		return this.propagated.keySet();
	}

	public Set<SootMethod> getPropagatedSources(SootClass exception) {
		return this.propagated.get(exception);
	}
	
	public Map<SootClass,Set<SootMethod>> getRethrownMap(){
		return this.rethrown;
	}
	
	public Set<SootClass> getAllRethrownExceptions(){
		return this.rethrown.keySet();
	}
	
	public Set<SootMethod> getRethrownSources(SootClass exception) {
		return this.rethrown.get(exception);
	}

	@Override
	public String getName() {
		return TAG_NAME;
	}

	@Override
	public byte[] getValue() throws AttributeValueException {
		return this.toString().getBytes();
	}

	@Override
	public String toString() {
		Iterator<SootClass> excepts = this.getThrowableSet().iterator();
		StringBuffer sb = new StringBuffer("{");
		while (excepts.hasNext()) {
			SootClass next = excepts.next();
			sb.append(next.getName() + ",");
		}
		if (sb.length() > 1)
			sb.deleteCharAt(sb.length() - 1);
		sb.append("}");
		return sb.toString();
	}

	private void putInMap(Map<SootClass, Set<SootMethod>> map, SootClass exception,
			SootMethod source) {
		if (map.containsKey(exception)) {
			Set<SootMethod> sources = map.get(exception);
			sources.add(source);
		} else {
			Set<SootMethod> sources = new HashSet<SootMethod>();
			sources.add(source);
			map.put(exception, sources);
		}
	}
}
